Project 2: Explore Bikeshare Data

Get Ready

library(plyr)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:plyr’:

    arrange, count, desc, failwith, id, mutate, rename, summarise, summarize

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggpubr)
Loading required package: ggplot2
Loading required package: magrittr

Attaching package: ‘ggpubr’

The following object is masked from ‘package:plyr’:

    mutate
library(tidyr)

Attaching package: ‘tidyr’

The following object is masked from ‘package:magrittr’:

    extract
library(ggplot2)
library(lubridate)
package ‘lubridate’ was built under R version 3.6.2
Attaching package: ‘lubridate’

The following objects are masked from ‘package:dplyr’:

    intersect, setdiff, union

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
library(gridExtra)

Attaching package: ‘gridExtra’

The following object is masked from ‘package:dplyr’:

    combine

Load data sets

# Load data sets
ny <- read.csv("new_york_city.csv")
wash <- read.csv("washington.csv")
chi <- read.csv("chicago.csv")

Inspect the New York City data set

# Inspect the New York City data set
head(ny)
dim(ny)
[1] 54770     9
colnames(ny)
[1] "X"             "Start.Time"    "End.Time"      "Trip.Duration" "Start.Station" "End.Station"   "User.Type"    
[8] "Gender"        "Birth.Year"   
str(ny)
'data.frame':   54770 obs. of  9 variables:
 $ X            : int  5688089 4096714 2173887 3945638 6208972 1285652 1675753 1692245 2271331 1558339 ...
 $ Start.Time   : Factor w/ 54568 levels "2017-01-01 00:17:01",..: 45448 32799 17316 31589 49688 10220 13390 13509 18111 12449 ...
 $ End.Time     : Factor w/ 54562 levels "201","2017-01-01 00:30:56",..: 45432 32783 17295 31567 49668 10204 13364 13505 18092 12422 ...
 $ Trip.Duration: int  795 692 1325 703 329 998 478 4038 5132 309 ...
 $ Start.Station: Factor w/ 636 levels "","1 Ave & E 16 St",..: 522 406 10 93 5 521 325 309 151 245 ...
 $ End.Station  : Factor w/ 638 levels "","1 Ave & E 16 St",..: 613 8 362 558 269 107 389 110 151 243 ...
 $ User.Type    : Factor w/ 3 levels "","Customer",..: 3 3 3 3 3 3 3 3 2 3 ...
 $ Gender       : Factor w/ 3 levels "","Female","Male": 3 3 3 2 3 3 3 3 1 3 ...
 $ Birth.Year   : num  1998 1981 1987 1986 1992 ...
summary(ny)
       X                         Start.Time                   End.Time     Trip.Duration                     Start.Station  
 Min.   :     47   2017-05-11 18:26:10:    3   2017-01-03 08:54:10:    2   Min.   :     61.0   Pershing Square North:  592  
 1st Qu.:1712425   2017-01-04 13:58:24:    2   2017-01-04 17:21:55:    2   1st Qu.:    368.0   W 21 St & 6 Ave      :  385  
 Median :3418634   2017-01-09 09:36:01:    2   2017-01-05 17:25:17:    2   Median :    610.0   Broadway & E 22 St   :  383  
 Mean   :3415873   2017-01-21 15:36:56:    2   2017-01-12 08:34:01:    2   Mean   :    903.6   E 17 St & Broadway   :  380  
 3rd Qu.:5123382   2017-01-21 17:49:59:    2   2017-01-12 09:41:54:    2   3rd Qu.:   1051.0   West St & Chambers St:  364  
 Max.   :6816152   2017-01-21 20:08:29:    2   2017-01-12 20:34:42:    2   Max.   :1088634.0   W 20 St & 11 Ave     :  329  
                   (Other)            :54757   (Other)            :54758   NA's   :1           (Other)              :52337  
                End.Station         User.Type        Gender        Birth.Year  
 Pershing Square North:  556             :  119         : 5410   Min.   :1885  
 E 17 St & Broadway   :  445   Customer  : 5558   Female:12159   1st Qu.:1970  
 Broadway & E 22 St   :  427   Subscriber:49093   Male  :37201   Median :1981  
 W 21 St & 6 Ave      :  365                                     Mean   :1978  
 W 20 St & 11 Ave     :  344                                     3rd Qu.:1988  
 W 38 St & 8 Ave      :  338                                     Max.   :2001  
 (Other)              :52295                                     NA's   :5218  

Inspect the Washington D.C. data set

# Inspect the Washington D.C. data set
head(wash)
dim(wash)
[1] 89051     7
colnames(wash)
[1] "X"             "Start.Time"    "End.Time"      "Trip.Duration" "Start.Station" "End.Station"   "User.Type"    
str(wash)
'data.frame':   89051 obs. of  7 variables:
 $ X            : int  1621326 482740 1330037 665458 1481135 1148202 1594275 1601832 574182 327058 ...
 $ Start.Time   : Factor w/ 81223 levels "","2017-01-01 00:11:00",..: 74753 19510 59964 26708 67716 50891 73381 73775 23142 13333 ...
 $ End.Time     : Factor w/ 81217 levels "","2017-01-01 00:14:00",..: 74744 19473 59981 26732 67753 50918 73397 73775 23114 13350 ...
 $ Trip.Duration: num  489 403 637 1827 1549 ...
 $ Start.Station: Factor w/ 478 levels "","10th & E St NW",..: 27 478 66 221 278 84 368 82 71 60 ...
 $ End.Station  : Factor w/ 479 levels "","10th & E St NW",..: 47 219 144 312 315 239 162 376 51 308 ...
 $ User.Type    : Factor w/ 3 levels "","Customer",..: 3 3 3 2 3 3 3 3 3 3 ...
summary(wash)
       X                         Start.Time                   End.Time     Trip.Duration     
 Min.   :      7   2017-02-19 12:19:00:    6   2017-03-09 17:54:00:    7   Min.   :    60.3  
 1st Qu.: 434587   2017-02-20 11:35:00:    6   2017-03-28 18:11:00:    7   1st Qu.:   410.9  
 Median : 872858   2017-02-24 17:46:00:    6   2017-01-13 17:48:00:    6   Median :   707.0  
 Mean   : 873881   2017-03-01 08:20:00:    6   2017-01-31 08:49:00:    6   Mean   :  1234.0  
 3rd Qu.:1313305   2017-03-02 08:39:00:    6   2017-02-13 18:09:00:    6   3rd Qu.:  1233.2  
 Max.   :1751392   2017-03-09 17:31:00:    6   2017-02-20 11:38:00:    6   Max.   :904591.4  
                   (Other)            :89015   (Other)            :89013   NA's   :1         
                              Start.Station                                                  End.Station         User.Type    
 Columbus Circle / Union Station     : 1700   Columbus Circle / Union Station                      : 1767             :    1  
 Lincoln Memorial                    : 1546   Jefferson Dr & 14th St SW                            : 1603   Customer  :23450  
 Jefferson Dr & 14th St SW           : 1488   Lincoln Memorial                                     : 1514   Subscriber:65600  
 Massachusetts Ave & Dupont Circle NW: 1219   Massachusetts Ave & Dupont Circle NW                 : 1344                     
 Jefferson Memorial                  : 1068   Smithsonian-National Mall / Jefferson Dr & 12th St SW: 1103                     
 15th & P St NW                      : 1040   15th & P St NW                                       : 1077                     
 (Other)                             :80990   (Other)                                              :80643                     

Inspect the Chicago data set

# Inspect the Chicago data set
head(chi)
dim(chi)
[1] 8630    9
colnames(chi)
[1] "X"             "Start.Time"    "End.Time"      "Trip.Duration" "Start.Station" "End.Station"   "User.Type"    
[8] "Gender"        "Birth.Year"   
str(chi)
'data.frame':   8630 obs. of  9 variables:
 $ X            : int  1423854 955915 9031 304487 45207 1473887 961916 65924 606841 135470 ...
 $ Start.Time   : Factor w/ 8624 levels "2017-01-01 00:40:14",..: 7876 5303 73 1721 267 8173 5347 368 3376 795 ...
 $ End.Time     : Factor w/ 8625 levels "2017-01-01 00:46:32",..: 7876 5303 73 1722 267 8173 5346 368 3376 796 ...
 $ Trip.Duration: int  321 1610 416 350 534 586 281 723 689 493 ...
 $ Start.Station: Factor w/ 472 levels "2112 W Peterson Ave",..: 468 424 291 80 103 119 22 255 374 420 ...
 $ End.Station  : Factor w/ 471 levels "","2112 W Peterson Ave",..: 132 381 469 409 151 70 467 251 200 118 ...
 $ User.Type    : Factor w/ 3 levels "","Customer",..: 3 3 3 3 3 3 3 2 3 3 ...
 $ Gender       : Factor w/ 3 levels "","Female","Male": 3 2 3 3 3 3 2 1 3 3 ...
 $ Birth.Year   : num  1992 1992 1981 1986 1975 ...
summary(chi)
       X                         Start.Time                  End.Time    Trip.Duration                          Start.Station 
 Min.   :     36   2017-01-24 07:40:32:   2   2017-04-16 13:16:52:   2   Min.   :   60.0   Streeter Dr & Grand Ave     : 210  
 1st Qu.: 386722   2017-04-22 13:16:25:   2   2017-04-26 16:29:26:   2   1st Qu.:  394.2   Lake Shore Dr & Monroe St   : 140  
 Median : 773554   2017-05-27 15:17:50:   2   2017-05-21 16:20:56:   2   Median :  670.0   Clinton St & Washington Blvd: 120  
 Mean   : 776721   2017-06-10 13:29:41:   2   2017-05-27 09:58:21:   2   Mean   :  937.2   Clinton St & Madison St     : 102  
 3rd Qu.:1171266   2017-06-20 17:05:11:   2   2017-06-25 14:51:35:   2   3rd Qu.: 1119.0   Canal St & Adams St         : 101  
 Max.   :1551248   2017-06-21 13:18:52:   2   2017-01-01 00:46:32:   1   Max.   :85408.0   Michigan Ave & Oak St       :  98  
                   (Other)            :8618   (Other)            :8619                     (Other)                     :7859  
                       End.Station        User.Type       Gender       Birth.Year  
 Streeter Dr & Grand Ave     : 233             :   1         :1748   Min.   :1899  
 Clinton St & Madison St     : 145   Customer  :1746   Female:1723   1st Qu.:1975  
 Theater on the Lake         : 131   Subscriber:6883   Male  :5159   Median :1984  
 Lake Shore Dr & Monroe St   : 115                                   Mean   :1981  
 Clinton St & Washington Blvd: 109                                   3rd Qu.:1989  
 Lake Shore Dr & North Blvd  : 102                                   Max.   :2002  
 (Other)                     :7795                                   NA's   :1747  

Preparations (Joining the Data Sets)

Before joining the data sets, I include the variable city for each of the original data sets in order to be able to identify which city the observations belong to later on. In addition, I also want to exclude all observations with missing values.

Built function to include variable city and exclude all observations with missing values

# Built function to include variable city and exclude all observations with missing values
city_omit <- function(x) {
  city_name <- deparse(substitute(x))
  x%>%
  mutate(city = city_name)%>%
  na.omit()
}

Use function to include variable city and exclude all observations with missing values for each city

# Use function to include variable city and exclude all observations with missing values for each city
ny <- city_omit(ny)
wash <- city_omit(wash)
chi <- city_omit(chi)

Check results of the function

# Check results of the function
head(ny)
dim(ny)
[1] 49552    10
head(wash)
dim(wash)
[1] 89050     8
head(chi)
dim(chi)
[1] 6883   10

Use plyr’s rbind.fill() function to join all three data sets (Union)

# Use plyr's rbind.fill() function to join all three data sets (Union)
bikeshare <- rbind.fill(ny, wash, chi)
head(bikeshare)
tail(bikeshare)

Build function to check if the sizes of joined data sets are equal

### Build function to check if sizes of joined data sets are equal
check <- function(x, y) {
  ifelse(x == y, print("The size of the data sets is equal"), print("Error"))
}

Quickly check if the size of the joined data set is equal to the sum of the individual data sets

# Quickly check if the size of the joined data set is equal to the sum of the individual data sets
x1 <- nrow(bikeshare)
y1 <- nrow(ny) + nrow(wash) + nrow(chi)
check(x1, y1)
[1] "The size of the data sets is equal"
[1] "The size of the data sets is equal"

Add id column to uniquely identify each observation

# Add id column to uniquely identify each observation
bikeshare$id <- seq.int(nrow(bikeshare))
head(bikeshare)
tail(bikeshare)

Question 1

Which city is using the bikeshare service for the longest trip duration on average? In other words, what is the average travel time for users in different cities?

Due to the fact, that the variable Trip.Duration is declared in seconds, create a new variable that transforms Trip.Duration from seconds to minutes

# Due to the fact, that the variable Trip.Duration is declared in seconds, create a new variable that transforms
# Trip.Duration from seconds to minutes
bikeshare$minutes <-  round((bikeshare$Trip.Duration / 60), 2)
head(bikeshare)

Create plot showing the distribution of trip durations in minutes for each of the three cities, as well as their mean and median

# Create plot showing the distribution of trip durations in minutes for each of the three cities, as well as their mean and median
ggplot(aes(x=minutes), data=bikeshare) +
  geom_histogram(binwidth = 0.5, color = "white", fill = "grey26") +
  coord_cartesian(xlim=c(0, quantile(bikeshare$minutes, 0.95))) + # Omit top 5% outliers
  scale_x_continuous(breaks = seq(0, 45, 5)) +
  labs(title="Distribution of Trip Duration (in Minutes) per City", caption = "Median = Blue \n Mean = Red") +
  xlab("Trip Duration in Minutes") + ylab("Frequency") +
  facet_wrap(~city, nrow = 3, scales="free_y", labeller = as_labeller(c(chi = "Chicago", ny = "New York", wash = "Washington"))) +
  stat_central_tendency(type = "mean", color = "red", show.legend = TRUE) +
  stat_central_tendency(type = "median", color = "blue", show.legend = TRUE)

Create boxplot to see how long the majority of customers uses the bikesharing serivce

# Create a boxplot to see how long the majority of customers uses the bikesharing service
ggplot(aes(x=minutes), data=bikeshare) +
  geom_boxplot() +
  coord_cartesian(xlim=c(0, quantile(bikeshare$minutes, 0.95))) + # Omit top 5% outliers
  xlab("Trip Duration in Minutes") +
  scale_x_continuous(breaks=seq(0, 45, 5)) +
  facet_wrap(~city, nrow = 3, labeller = as_labeller(c(chi = "Chicago", ny = "New York", wash = "Washington")))

Calculate summary statistcs

# Calculate summary statistcs
bikeshare_summary <- bikeshare%>%
  group_by(city)%>%
  summarise(mean = mean(minutes),
            median = median(minutes),
            min(minutes),
            max(minutes),
            observations = length(minutes))
bikeshare_summary

Answer: Customers in Washington D.C. use the bikesharing service for the longest trip duration, namely for roughly 20min and 30 seconds on average. In comparision, customers in New York City use the service on average 13min and 15 seconds, while people in Chiacgo take the shortest trips with 11min and 30 seconds.

The median for each of the cities is less spread out. The median customer uses the service almost equally in Chicago and New York with a little less than 10 minutes, while the median customer in Washington still uses the service for roughly 12 minutes.

The difference between mean and median can be explained, due to the fact that more customers in Washington use the bike service for longer trips, as is visible by the longer tail in the distribution.

The boxplots shows that the majority of customers (25th to 75 percentile) in New York and Chicago use the bike sharing service for a trip duration between 5 and 17 minutes, while in Washington D.C. the majority of customers use the service between 7 and 21 minutes.

Question 2

Does age have an effect on the duration of a costumer’s trip?

Create a variable age using the existing variable Birth.Year

# Create a variable age using the existing variable Birth.Year
bikeshare$age <- 2020 - bikeshare$Birth.Year
head(bikeshare)

Create new data frame without missing values for Washington

# Create new data frame without missing values for Washington
bikeshare_age <- bikeshare%>%
  na.omit()
head(bikeshare_age)

Create scatter plot showing the correlation between age and trip duration in minutes

# Create scatter plot showing the correlation between age and trip duration in minutes
ggplot(aes(x=age, y=minutes, color=city), data=bikeshare_age) +
  geom_jitter() +
  geom_smooth(method = "lm", color = "black", linetype = "dashed") +
  ggtitle("Correlation between Customer Age and Trip Duration") +
  coord_cartesian(xlim=c(15, quantile(bikeshare_age$age, 0.9995)),
                  ylim=c(0, quantile(bikeshare_age$minutes, 0.9995))) +
  scale_x_continuous(breaks=seq(15, 100, 5)) +
  xlab("Age") + ylab("Trip Duration (in Minutes)") +
  scale_color_discrete(name = "City", labels = c("Chicago", "New York"))

Calculate actual correlation

# Calculate actual correlation
round(cor(bikeshare_age$age, bikeshare_age$minutes), 5)
[1] -0.00132

The actual correlation coefficient for the two variables trip duration (in minutes) and age is close to 0.

There also seems to be no difference between the cities of Chicago and New York. These are the only two cities for which data is available.

Question 3

What is the most common hour of the day for customers to use the bikesharing serivce?

Extract both the hour in which a trip started, as well as ended from Start.Time and End.Time respectively

# Extract both the hour in which a trip started, as well as ended from Start.Time and End.Time respectively
bikeshare$hour_start <- hour(bikeshare$Start.Time)
tz(): Don't know how to compute timezone for object of class factor; returning "UTC". This warning will become an error in the next major version of lubridate.
bikeshare$hour_end <- hour(bikeshare$End.Time)
tz(): Don't know how to compute timezone for object of class factor; returning "UTC". This warning will become an error in the next major version of lubridate.
head(bikeshare)

Create histogram for tje start of the trip to see when most customers start their trips during the day

# Create histogram for tje start of the trip to see when most customers start their trips during the day
q3_1 <- ggplot(aes(hour_start), data=bikeshare) +
  geom_histogram(binwidth = 1, color = "white", fill = "blue") +
  scale_x_continuous(breaks=seq(0, 23, 1)) +
  theme(axis.text=element_text(size=6)) +
  xlab("Hour of the Day") + ylab("Number of Started Trips") +
  facet_wrap(~city, nrow = 3, scales="free_y", labeller = as_labeller(c(chi = "Chicago", ny = "New York", wash = "Washington")))
q3_1

Create histogram for the end of the trip to see when most customers end their trips at any given day

# Create histogram for the end of the trip to see when most customers end their trips at any given day
q3_2 <- ggplot(aes(hour_end), data=bikeshare) +
  geom_histogram(binwidth = 1, color = "white", fill = "green") +
  scale_x_continuous(breaks=seq(0, 23, 1)) +
  theme(axis.text=element_text(size=6)) +
  xlab("Hour of the Day") + ylab("Number of Ended Trips") +
  facet_wrap(~city, nrow = 3, scales="free_y", labeller = as_labeller(c(chi = "Chicago", ny = "New York", wash = "Washington")))
q3_2

Combine both plots to see the busiest times for the use of the bikeshare service per city and hour of the day

# Combine both plots to see the busiest times for the use of the bikeshare service per city and hour of the day
grid.arrange(q3_1, q3_2, ncol = 2)

Combine both histogram in one plot to view the busiest time combined

# Combine both histogram in one plot to view the busiest time combined
ggplot(aes(hour_start), data=bikeshare) +
  geom_histogram(binwidth = 1, color = "white", fill = "blue", alpha = 0.4, position="identity") +
  geom_histogram(aes(x=hour_end), data=bikeshare, binwidth = 1, color = "white", fill = "green", alpha = 0.4, position="identity") +
  scale_x_continuous(breaks=seq(0, 23, 1)) +
  xlab("Hour of the Day") + ylab("Number of Trips") +
  facet_wrap(~city, nrow = 3, scales="free_y", labeller = as_labeller(c(chi = "Chicago", ny = "New York", wash = "Washington")))

Count the number of trips that STARTED in each hour of the day by city

# Count the number of trips that STARTED in each hour of the day by city
hours_start <- bikeshare%>%
  group_by(city, hour_start)%>%
  summarise(n_start = length(id))
hours_start

Extract the hour of the day with the maximum amount of trips STARTED

# Extract the hour of the day with the maximum amount of trips STARTED
max_start_hours <- hours_start%>%
  group_by(city)%>%
  filter(n_start==max(n_start))
max_start_hours

Count the number of trips that END in each hour of the day by city

# Count the number of trips that END in each hour of the day by city
hours_end <- bikeshare%>%
  group_by(city, hour_end)%>%
  summarise(n_end = length(id))
hours_end

Extract the hour of the day, where most of the trips END

# Extract the hour of the day, where most of the trips END
max_end_hours <- hours_end%>%
  group_by(city)%>%
  filter(n_end==max(n_end))
max_end_hours

Combine data sets containing the maximum values to provide summary statistics

# Combine data sets containing the maximum values to provide summary statistics
max_hours <- max_start_hours%>%
  full_join(max_end_hours, by = "city")
max_hours

Answer: The hour of the day, when the bikesharing service is most used, is in the afternoon for both Chicago and New York City. In both cities the busiest hour of the day is between 5pm and 6pm with roughly 5050 trips starting and ending in New York City and roughly 900 in Chicago.

In comparision, customers in Washington D.C. are more inclined to use the bikesharing service in the mornings with 8am being prime time with almost 10.000 trips.

Question 4

What is the most common trip from start to end by city and what is the average travel time on this trip?

LS0tCnRpdGxlOiAiVWRhY2l0eSBOYW5vZGVncmVlIFByb2dyYW1taW5nIGZvciBEYXRhIFNjaWVuY2Ugd2l0aCBSIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiBZYW5uaWsgU2Fzc21hbm4KLS0tCgojICBQcm9qZWN0IDI6IEV4cGxvcmUgQmlrZXNoYXJlIERhdGEKCiMjIEdldCBSZWFkeQpgYGB7cn0KIyBTZXQgd29yayBkaXJlY3RvcnkgYW5kIGxvYWQgbGlicmFyaWVzCnNldHdkKCIvVXNlcnMveWFubmlrc2Fzc21hbm4vRGVza3RvcC9EYXRhX1NjaWVuY2UvUi9VZGFjaXR5X05hbm9kZWdyZWVfRGF0YV9TY2llbmNlX3dpdGhfUi9SX1Byb2plY3QiKQoKaW5zdGFsbC5wYWNrYWdlcygicGx5ciIpCmluc3RhbGwucGFja2FnZXMoImRwbHlyIikKaW5zdGFsbC5wYWNrYWdlcygiZ2dwdWJyIikKaW5zdGFsbC5wYWNrYWdlcygidGlkeXIiKQppbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKaW5zdGFsbC5wYWNrYWdlcygibHVicmlkYXRlIikKaW5zdGFsbC5wYWNrYWdlcygiZ3JpZEV4dHJhIikKbGlicmFyeShwbHlyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeSh0aWR5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShncmlkRXh0cmEpCmBgYAoKIyMjIExvYWQgZGF0YSBzZXRzCmBgYHtyfQojIExvYWQgZGF0YSBzZXRzCm55IDwtIHJlYWQuY3N2KCJuZXdfeW9ya19jaXR5LmNzdiIpCndhc2ggPC0gcmVhZC5jc3YoIndhc2hpbmd0b24uY3N2IikKY2hpIDwtIHJlYWQuY3N2KCJjaGljYWdvLmNzdiIpCmBgYAoKIyMjIEluc3BlY3QgdGhlIE5ldyBZb3JrIENpdHkgZGF0YSBzZXQKYGBge3J9CiMgSW5zcGVjdCB0aGUgTmV3IFlvcmsgQ2l0eSBkYXRhIHNldApoZWFkKG55KQpkaW0obnkpCmNvbG5hbWVzKG55KQpzdHIobnkpCnN1bW1hcnkobnkpCmBgYAoKIyMjIEluc3BlY3QgdGhlIFdhc2hpbmd0b24gRC5DLiBkYXRhIHNldApgYGB7cn0KIyBJbnNwZWN0IHRoZSBXYXNoaW5ndG9uIEQuQy4gZGF0YSBzZXQKaGVhZCh3YXNoKQpkaW0od2FzaCkKY29sbmFtZXMod2FzaCkKc3RyKHdhc2gpCnN1bW1hcnkod2FzaCkKYGBgCgojIyMgSW5zcGVjdCB0aGUgQ2hpY2FnbyBkYXRhIHNldApgYGB7cn0KIyBJbnNwZWN0IHRoZSBDaGljYWdvIGRhdGEgc2V0CmhlYWQoY2hpKQpkaW0oY2hpKQpjb2xuYW1lcyhjaGkpCnN0cihjaGkpCnN1bW1hcnkoY2hpKQpgYGAKCgojIyBQcmVwYXJhdGlvbnMgKEpvaW5pbmcgdGhlIERhdGEgU2V0cykKCiMjIyBCZWZvcmUgam9pbmluZyB0aGUgZGF0YSBzZXRzLCBJIGluY2x1ZGUgdGhlIHZhcmlhYmxlIGNpdHkgZm9yIGVhY2ggb2YgdGhlIG9yaWdpbmFsIGRhdGEgc2V0cyBpbiBvcmRlciB0byBiZSBhYmxlIHRvIGlkZW50aWZ5IHdoaWNoIGNpdHkgdGhlIG9ic2VydmF0aW9ucyBiZWxvbmcgdG8gbGF0ZXIgb24uIEluIGFkZGl0aW9uLCBJIGFsc28gd2FudCB0byBleGNsdWRlIGFsbCBvYnNlcnZhdGlvbnMgd2l0aCBtaXNzaW5nIHZhbHVlcy4KCiMjIyBCdWlsdCBmdW5jdGlvbiB0byBpbmNsdWRlIHZhcmlhYmxlIGNpdHkgYW5kIGV4Y2x1ZGUgYWxsIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcgdmFsdWVzCmBgYHtyfQojIEJ1aWx0IGZ1bmN0aW9uIHRvIGluY2x1ZGUgdmFyaWFibGUgY2l0eSBhbmQgZXhjbHVkZSBhbGwgb2JzZXJ2YXRpb25zIHdpdGggbWlzc2luZyB2YWx1ZXMKY2l0eV9vbWl0IDwtIGZ1bmN0aW9uKHgpIHsKICBjaXR5X25hbWUgPC0gZGVwYXJzZShzdWJzdGl0dXRlKHgpKQogIHglPiUKICBtdXRhdGUoY2l0eSA9IGNpdHlfbmFtZSklPiUKICBuYS5vbWl0KCkKfQpgYGAKCiMjIyBVc2UgZnVuY3Rpb24gdG8gaW5jbHVkZSB2YXJpYWJsZSBjaXR5IGFuZCBleGNsdWRlIGFsbCBvYnNlcnZhdGlvbnMgd2l0aCBtaXNzaW5nIHZhbHVlcyBmb3IgZWFjaCBjaXR5CmBgYHtyfQojIFVzZSBmdW5jdGlvbiB0byBpbmNsdWRlIHZhcmlhYmxlIGNpdHkgYW5kIGV4Y2x1ZGUgYWxsIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcgdmFsdWVzIGZvciBlYWNoIGNpdHkKbnkgPC0gY2l0eV9vbWl0KG55KQp3YXNoIDwtIGNpdHlfb21pdCh3YXNoKQpjaGkgPC0gY2l0eV9vbWl0KGNoaSkKYGBgCgojIyMgQ2hlY2sgcmVzdWx0cyBvZiB0aGUgZnVuY3Rpb24KYGBge3J9CiMgQ2hlY2sgcmVzdWx0cyBvZiB0aGUgZnVuY3Rpb24KaGVhZChueSkKZGltKG55KQpoZWFkKHdhc2gpCmRpbSh3YXNoKQpoZWFkKGNoaSkKZGltKGNoaSkKYGBgCgojIyMgVXNlIHBseXIncyByYmluZC5maWxsKCkgZnVuY3Rpb24gdG8gam9pbiBhbGwgdGhyZWUgZGF0YSBzZXRzIChVbmlvbikKYGBge3J9CiMgVXNlIHBseXIncyByYmluZC5maWxsKCkgZnVuY3Rpb24gdG8gam9pbiBhbGwgdGhyZWUgZGF0YSBzZXRzIChVbmlvbikKYmlrZXNoYXJlIDwtIHJiaW5kLmZpbGwobnksIHdhc2gsIGNoaSkKaGVhZChiaWtlc2hhcmUpCnRhaWwoYmlrZXNoYXJlKQpgYGAKIyMjIEJ1aWxkIGZ1bmN0aW9uIHRvIGNoZWNrIGlmIHRoZSBzaXplcyBvZiBqb2luZWQgZGF0YSBzZXRzIGFyZSBlcXVhbApgYGB7cn0KIyMjIEJ1aWxkIGZ1bmN0aW9uIHRvIGNoZWNrIGlmIHNpemVzIG9mIGpvaW5lZCBkYXRhIHNldHMgYXJlIGVxdWFsCmNoZWNrIDwtIGZ1bmN0aW9uKHgsIHkpIHsKICBpZmVsc2UoeCA9PSB5LCBwcmludCgiVGhlIHNpemUgb2YgdGhlIGRhdGEgc2V0cyBpcyBlcXVhbCIpLCBwcmludCgiRXJyb3IiKSkKfQpgYGAKCiMjIyBRdWlja2x5IGNoZWNrIGlmIHRoZSBzaXplIG9mIHRoZSBqb2luZWQgZGF0YSBzZXQgaXMgZXF1YWwgdG8gdGhlIHN1bSBvZiB0aGUgaW5kaXZpZHVhbCBkYXRhIHNldHMKYGBge3J9CiMgUXVpY2tseSBjaGVjayBpZiB0aGUgc2l6ZSBvZiB0aGUgam9pbmVkIGRhdGEgc2V0IGlzIGVxdWFsIHRvIHRoZSBzdW0gb2YgdGhlIGluZGl2aWR1YWwgZGF0YSBzZXRzCngxIDwtIG5yb3coYmlrZXNoYXJlKQp5MSA8LSBucm93KG55KSArIG5yb3cod2FzaCkgKyBucm93KGNoaSkKY2hlY2soeDEsIHkxKQpgYGAKCiMjIyBBZGQgaWQgY29sdW1uIHRvIHVuaXF1ZWx5IGlkZW50aWZ5IGVhY2ggb2JzZXJ2YXRpb24KYGBge3J9CiMgQWRkIGlkIGNvbHVtbiB0byB1bmlxdWVseSBpZGVudGlmeSBlYWNoIG9ic2VydmF0aW9uCmJpa2VzaGFyZSRpZCA8LSBzZXEuaW50KG5yb3coYmlrZXNoYXJlKSkKaGVhZChiaWtlc2hhcmUpCnRhaWwoYmlrZXNoYXJlKQpgYGAKCgojIFF1ZXN0aW9uIDEKIyMgV2hpY2ggY2l0eSBpcyB1c2luZyB0aGUgYmlrZXNoYXJlIHNlcnZpY2UgZm9yIHRoZSBsb25nZXN0IHRyaXAgZHVyYXRpb24gb24gYXZlcmFnZT8gSW4gb3RoZXIgd29yZHMsIHdoYXQgaXMgdGhlIGF2ZXJhZ2UgdHJhdmVsIHRpbWUgZm9yIHVzZXJzIGluIGRpZmZlcmVudCBjaXRpZXM/CgojIyMgRHVlIHRvIHRoZSBmYWN0LCB0aGF0IHRoZSB2YXJpYWJsZSBUcmlwLkR1cmF0aW9uIGlzIGRlY2xhcmVkIGluIHNlY29uZHMsIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSB0aGF0IHRyYW5zZm9ybXMgVHJpcC5EdXJhdGlvbiBmcm9tIHNlY29uZHMgdG8gbWludXRlcwpgYGB7cn0KIyBEdWUgdG8gdGhlIGZhY3QsIHRoYXQgdGhlIHZhcmlhYmxlIFRyaXAuRHVyYXRpb24gaXMgZGVjbGFyZWQgaW4gc2Vjb25kcywgY3JlYXRlIGEgbmV3IHZhcmlhYmxlIHRoYXQgdHJhbnNmb3JtcwojIFRyaXAuRHVyYXRpb24gZnJvbSBzZWNvbmRzIHRvIG1pbnV0ZXMKYmlrZXNoYXJlJG1pbnV0ZXMgPC0gIHJvdW5kKChiaWtlc2hhcmUkVHJpcC5EdXJhdGlvbiAvIDYwKSwgMikKaGVhZChiaWtlc2hhcmUpCmBgYAoKIyMjIENyZWF0ZSBwbG90IHNob3dpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0cmlwIGR1cmF0aW9ucyBpbiBtaW51dGVzIGZvciBlYWNoIG9mIHRoZSB0aHJlZSBjaXRpZXMsIGFzIHdlbGwgYXMgdGhlaXIgbWVhbiBhbmQgbWVkaWFuCmBgYHtyfQojIENyZWF0ZSBwbG90IHNob3dpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0cmlwIGR1cmF0aW9ucyBpbiBtaW51dGVzIGZvciBlYWNoIG9mIHRoZSB0aHJlZSBjaXRpZXMsIGFzIHdlbGwgYXMgdGhlaXIgbWVhbiBhbmQgbWVkaWFuCmdncGxvdChhZXMoeD1taW51dGVzKSwgZGF0YT1iaWtlc2hhcmUpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuNSwgY29sb3IgPSAid2hpdGUiLCBmaWxsID0gImdyZXkyNiIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsIHF1YW50aWxlKGJpa2VzaGFyZSRtaW51dGVzLCAwLjk1KSkpICsgIyBPbWl0IHRvcCA1JSBvdXRsaWVycwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgNDUsIDUpKSArCiAgbGFicyh0aXRsZT0iRGlzdHJpYnV0aW9uIG9mIFRyaXAgRHVyYXRpb24gKGluIE1pbnV0ZXMpIHBlciBDaXR5IiwgY2FwdGlvbiA9ICJNZWRpYW4gPSBCbHVlIFxuIE1lYW4gPSBSZWQiKSArCiAgeGxhYigiVHJpcCBEdXJhdGlvbiBpbiBNaW51dGVzIikgKyB5bGFiKCJGcmVxdWVuY3kiKSArCiAgZmFjZXRfd3JhcCh+Y2l0eSwgbnJvdyA9IDMsIHNjYWxlcz0iZnJlZV95IiwgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihjKGNoaSA9ICJDaGljYWdvIiwgbnkgPSAiTmV3IFlvcmsiLCB3YXNoID0gIldhc2hpbmd0b24iKSkpICsKICBzdGF0X2NlbnRyYWxfdGVuZGVuY3kodHlwZSA9ICJtZWFuIiwgY29sb3IgPSAicmVkIiwgc2hvdy5sZWdlbmQgPSBUUlVFKSArCiAgc3RhdF9jZW50cmFsX3RlbmRlbmN5KHR5cGUgPSAibWVkaWFuIiwgY29sb3IgPSAiYmx1ZSIsIHNob3cubGVnZW5kID0gVFJVRSkKYGBgCgojIyMgQ3JlYXRlIGJveHBsb3QgdG8gc2VlIGhvdyBsb25nIHRoZSBtYWpvcml0eSBvZiBjdXN0b21lcnMgdXNlcyB0aGUgYmlrZXNoYXJpbmcgc2VyaXZjZQpgYGB7cn0KIyBDcmVhdGUgYSBib3hwbG90IHRvIHNlZSBob3cgbG9uZyB0aGUgbWFqb3JpdHkgb2YgY3VzdG9tZXJzIHVzZXMgdGhlIGJpa2VzaGFyaW5nIHNlcnZpY2UKZ2dwbG90KGFlcyh4PW1pbnV0ZXMpLCBkYXRhPWJpa2VzaGFyZSkgKwogIGdlb21fYm94cGxvdCgpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsIHF1YW50aWxlKGJpa2VzaGFyZSRtaW51dGVzLCAwLjk1KSkpICsgIyBPbWl0IHRvcCA1JSBvdXRsaWVycwogIHhsYWIoIlRyaXAgRHVyYXRpb24gaW4gTWludXRlcyIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLCA0NSwgNSkpICsKICBmYWNldF93cmFwKH5jaXR5LCBucm93ID0gMywgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihjKGNoaSA9ICJDaGljYWdvIiwgbnkgPSAiTmV3IFlvcmsiLCB3YXNoID0gIldhc2hpbmd0b24iKSkpCmBgYAoKCiMjIyBDYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0Y3MKYGBge3J9CiMgQ2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGNzCmJpa2VzaGFyZV9zdW1tYXJ5IDwtIGJpa2VzaGFyZSU+JQogIGdyb3VwX2J5KGNpdHkpJT4lCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKG1pbnV0ZXMpLAogICAgICAgICAgICBtZWRpYW4gPSBtZWRpYW4obWludXRlcyksCiAgICAgICAgICAgIG1pbihtaW51dGVzKSwKICAgICAgICAgICAgbWF4KG1pbnV0ZXMpLAogICAgICAgICAgICBvYnNlcnZhdGlvbnMgPSBsZW5ndGgobWludXRlcykpCmJpa2VzaGFyZV9zdW1tYXJ5CmBgYAoKIyMjIEFuc3dlcjogQ3VzdG9tZXJzIGluIFdhc2hpbmd0b24gRC5DLiB1c2UgdGhlIGJpa2VzaGFyaW5nIHNlcnZpY2UgZm9yIHRoZSBsb25nZXN0IHRyaXAgZHVyYXRpb24sIG5hbWVseSBmb3Igcm91Z2hseSAyMG1pbiBhbmQgMzAgc2Vjb25kcyBvbiBhdmVyYWdlLiBJbiBjb21wYXJpc2lvbiwgY3VzdG9tZXJzIGluIE5ldyBZb3JrIENpdHkgdXNlIHRoZSBzZXJ2aWNlIG9uIGF2ZXJhZ2UgMTNtaW4gYW5kIDE1IHNlY29uZHMsIHdoaWxlIHBlb3BsZSBpbiBDaGlhY2dvIHRha2UgdGhlIHNob3J0ZXN0IHRyaXBzIHdpdGggMTFtaW4gYW5kIDMwIHNlY29uZHMuCgojIyMgVGhlIG1lZGlhbiBmb3IgZWFjaCBvZiB0aGUgY2l0aWVzIGlzIGxlc3Mgc3ByZWFkIG91dC4gVGhlIG1lZGlhbiBjdXN0b21lciB1c2VzIHRoZSBzZXJ2aWNlIGFsbW9zdCBlcXVhbGx5IGluIENoaWNhZ28gYW5kIE5ldyBZb3JrIHdpdGggYSBsaXR0bGUgbGVzcyB0aGFuIDEwIG1pbnV0ZXMsIHdoaWxlIHRoZSBtZWRpYW4gY3VzdG9tZXIgaW4gV2FzaGluZ3RvbiBzdGlsbCB1c2VzIHRoZSBzZXJ2aWNlIGZvciByb3VnaGx5IDEyIG1pbnV0ZXMuCgojIyMgVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBtZWFuIGFuZCBtZWRpYW4gY2FuIGJlIGV4cGxhaW5lZCwgZHVlIHRvIHRoZSBmYWN0IHRoYXQgbW9yZSBjdXN0b21lcnMgaW4gV2FzaGluZ3RvbiB1c2UgdGhlIGJpa2Ugc2VydmljZSBmb3IgbG9uZ2VyIHRyaXBzLCBhcyBpcyB2aXNpYmxlIGJ5IHRoZSBsb25nZXIgdGFpbCBpbiB0aGUgZGlzdHJpYnV0aW9uLgoKIyMjIFRoZSBib3hwbG90cyBzaG93cyB0aGF0IHRoZSBtYWpvcml0eSBvZiBjdXN0b21lcnMgKDI1dGggdG8gNzUgcGVyY2VudGlsZSkgaW4gTmV3IFlvcmsgYW5kIENoaWNhZ28gdXNlIHRoZSBiaWtlIHNoYXJpbmcgc2VydmljZSBmb3IgYSB0cmlwIGR1cmF0aW9uIGJldHdlZW4gNSBhbmQgMTcgbWludXRlcywgd2hpbGUgaW4gV2FzaGluZ3RvbiBELkMuIHRoZSBtYWpvcml0eSBvZiBjdXN0b21lcnMgdXNlIHRoZSBzZXJ2aWNlIGJldHdlZW4gNyBhbmQgMjEgbWludXRlcy4KCgoKIyBRdWVzdGlvbiAyCiMjIERvZXMgYWdlIGhhdmUgYW4gZWZmZWN0IG9uIHRoZSBkdXJhdGlvbiBvZiBhIGNvc3R1bWVyJ3MgdHJpcD8KCiMjIyBDcmVhdGUgYSB2YXJpYWJsZSBhZ2UgdXNpbmcgdGhlIGV4aXN0aW5nIHZhcmlhYmxlIEJpcnRoLlllYXIKYGBge3J9CiMgQ3JlYXRlIGEgdmFyaWFibGUgYWdlIHVzaW5nIHRoZSBleGlzdGluZyB2YXJpYWJsZSBCaXJ0aC5ZZWFyCmJpa2VzaGFyZSRhZ2UgPC0gMjAyMCAtIGJpa2VzaGFyZSRCaXJ0aC5ZZWFyCmhlYWQoYmlrZXNoYXJlKQpgYGAKCiMjIyBDcmVhdGUgbmV3IGRhdGEgZnJhbWUgd2l0aG91dCBtaXNzaW5nIHZhbHVlcyBmb3IgV2FzaGluZ3RvbgpgYGB7cn0KIyBDcmVhdGUgbmV3IGRhdGEgZnJhbWUgd2l0aG91dCBtaXNzaW5nIHZhbHVlcyBmb3IgV2FzaGluZ3RvbgpiaWtlc2hhcmVfYWdlIDwtIGJpa2VzaGFyZSU+JQogIG5hLm9taXQoKQpoZWFkKGJpa2VzaGFyZV9hZ2UpCmBgYAoKIyMjIENyZWF0ZSBzY2F0dGVyIHBsb3Qgc2hvd2luZyB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBhZ2UgYW5kIHRyaXAgZHVyYXRpb24gaW4gbWludXRlcwpgYGB7cn0KIyBDcmVhdGUgc2NhdHRlciBwbG90IHNob3dpbmcgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gYWdlIGFuZCB0cmlwIGR1cmF0aW9uIGluIG1pbnV0ZXMKZ2dwbG90KGFlcyh4PWFnZSwgeT1taW51dGVzLCBjb2xvcj1jaXR5KSwgZGF0YT1iaWtlc2hhcmVfYWdlKSArCiAgZ2VvbV9qaXR0ZXIoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2d0aXRsZSgiQ29ycmVsYXRpb24gYmV0d2VlbiBDdXN0b21lciBBZ2UgYW5kIFRyaXAgRHVyYXRpb24iKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygxNSwgcXVhbnRpbGUoYmlrZXNoYXJlX2FnZSRhZ2UsIDAuOTk5NSkpLAogICAgICAgICAgICAgICAgICB5bGltPWMoMCwgcXVhbnRpbGUoYmlrZXNoYXJlX2FnZSRtaW51dGVzLCAwLjk5OTUpKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDE1LCAxMDAsIDUpKSArCiAgeGxhYigiQWdlIikgKyB5bGFiKCJUcmlwIER1cmF0aW9uIChpbiBNaW51dGVzKSIpICsKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShuYW1lID0gIkNpdHkiLCBsYWJlbHMgPSBjKCJDaGljYWdvIiwgIk5ldyBZb3JrIikpCmBgYAoKIyMjIENhbGN1bGF0ZSBhY3R1YWwgY29ycmVsYXRpb24KYGBge3J9CiMgQ2FsY3VsYXRlIGFjdHVhbCBjb3JyZWxhdGlvbgpyb3VuZChjb3IoYmlrZXNoYXJlX2FnZSRhZ2UsIGJpa2VzaGFyZV9hZ2UkbWludXRlcyksIDUpCmBgYAoKIyMjIEFuc3dlcjogVGhlcmUgc2VlbXMgdG8gYmUgbm8gY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgYWdlIG9mIGN1c3RvbWVycyBhbmQgdGhlaXIgdHJpcCBkdXJhdGlvbi4gQ29udHJhcnkgdG8gcG9wdWxhciBiZWxpZWYsIG9sZCBjdXN0b21lcnMgdGFrZSBhcyBsb25nIHRyaXBzIGFzIHlvdW5nIGN1c3RvbWVycyBkby4gSGVuY2UgdHJpcCBkdXJhdGlvbiBkb2Vzbid0IGRlY3JlYXNlIHdpdGggYWdlLgoKIyMjIFRoZSBhY3R1YWwgY29ycmVsYXRpb24gY29lZmZpY2llbnQgZm9yIHRoZSB0d28gdmFyaWFibGVzIHRyaXAgZHVyYXRpb24gKGluIG1pbnV0ZXMpIGFuZCBhZ2UgaXMgY2xvc2UgdG8gMC4KCiMjIyBUaGVyZSBhbHNvIHNlZW1zIHRvIGJlIG5vIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgY2l0aWVzIG9mIENoaWNhZ28gYW5kIE5ldyBZb3JrLiBUaGVzZSBhcmUgdGhlIG9ubHkgdHdvIGNpdGllcyBmb3Igd2hpY2ggZGF0YSBpcyBhdmFpbGFibGUuCgoKCiMgUXVlc3Rpb24gMwojIyBXaGF0IGlzIHRoZSBtb3N0IGNvbW1vbiBob3VyIG9mIHRoZSBkYXkgZm9yIGN1c3RvbWVycyB0byB1c2UgdGhlIGJpa2VzaGFyaW5nIHNlcml2Y2U/CgojIyMgRXh0cmFjdCBib3RoIHRoZSBob3VyIGluIHdoaWNoIGEgdHJpcCBzdGFydGVkLCBhcyB3ZWxsIGFzIGVuZGVkIGZyb20gU3RhcnQuVGltZSBhbmQgRW5kLlRpbWUgcmVzcGVjdGl2ZWx5CmBgYHtyfQojIEV4dHJhY3QgYm90aCB0aGUgaG91ciBpbiB3aGljaCBhIHRyaXAgc3RhcnRlZCwgYXMgd2VsbCBhcyBlbmRlZCBmcm9tIFN0YXJ0LlRpbWUgYW5kIEVuZC5UaW1lIHJlc3BlY3RpdmVseQpiaWtlc2hhcmUkaG91cl9zdGFydCA8LSBob3VyKGJpa2VzaGFyZSRTdGFydC5UaW1lKQpiaWtlc2hhcmUkaG91cl9lbmQgPC0gaG91cihiaWtlc2hhcmUkRW5kLlRpbWUpCmhlYWQoYmlrZXNoYXJlKQpgYGAKCiMjIyBDcmVhdGUgaGlzdG9ncmFtIGZvciB0amUgc3RhcnQgb2YgdGhlIHRyaXAgdG8gc2VlIHdoZW4gbW9zdCBjdXN0b21lcnMgc3RhcnQgdGhlaXIgdHJpcHMgZHVyaW5nIHRoZSBkYXkKYGBge3J9CiMgQ3JlYXRlIGhpc3RvZ3JhbSBmb3IgdGplIHN0YXJ0IG9mIHRoZSB0cmlwIHRvIHNlZSB3aGVuIG1vc3QgY3VzdG9tZXJzIHN0YXJ0IHRoZWlyIHRyaXBzIGR1cmluZyB0aGUgZGF5CnEzXzEgPC0gZ2dwbG90KGFlcyhob3VyX3N0YXJ0KSwgZGF0YT1iaWtlc2hhcmUpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gIndoaXRlIiwgZmlsbCA9ICJibHVlIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsIDIzLCAxKSkgKwogIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT02KSkgKwogIHhsYWIoIkhvdXIgb2YgdGhlIERheSIpICsgeWxhYigiTnVtYmVyIG9mIFN0YXJ0ZWQgVHJpcHMiKSArCiAgZmFjZXRfd3JhcCh+Y2l0eSwgbnJvdyA9IDMsIHNjYWxlcz0iZnJlZV95IiwgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihjKGNoaSA9ICJDaGljYWdvIiwgbnkgPSAiTmV3IFlvcmsiLCB3YXNoID0gIldhc2hpbmd0b24iKSkpCnEzXzEKYGBgCgojIyMgQ3JlYXRlIGhpc3RvZ3JhbSBmb3IgdGhlIGVuZCBvZiB0aGUgdHJpcCB0byBzZWUgd2hlbiBtb3N0IGN1c3RvbWVycyBlbmQgdGhlaXIgdHJpcHMgYXQgYW55IGdpdmVuIGRheQpgYGB7cn0KIyBDcmVhdGUgaGlzdG9ncmFtIGZvciB0aGUgZW5kIG9mIHRoZSB0cmlwIHRvIHNlZSB3aGVuIG1vc3QgY3VzdG9tZXJzIGVuZCB0aGVpciB0cmlwcyBhdCBhbnkgZ2l2ZW4gZGF5CnEzXzIgPC0gZ2dwbG90KGFlcyhob3VyX2VuZCksIGRhdGE9YmlrZXNoYXJlKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBjb2xvciA9ICJ3aGl0ZSIsIGZpbGwgPSAiZ3JlZW4iKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwgMjMsIDEpKSArCiAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpKSArCiAgeGxhYigiSG91ciBvZiB0aGUgRGF5IikgKyB5bGFiKCJOdW1iZXIgb2YgRW5kZWQgVHJpcHMiKSArCiAgZmFjZXRfd3JhcCh+Y2l0eSwgbnJvdyA9IDMsIHNjYWxlcz0iZnJlZV95IiwgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihjKGNoaSA9ICJDaGljYWdvIiwgbnkgPSAiTmV3IFlvcmsiLCB3YXNoID0gIldhc2hpbmd0b24iKSkpCnEzXzIKYGBgCgojIyMgQ29tYmluZSBib3RoIHBsb3RzIHRvIHNlZSB0aGUgYnVzaWVzdCB0aW1lcyBmb3IgdGhlIHVzZSBvZiB0aGUgYmlrZXNoYXJlIHNlcnZpY2UgcGVyIGNpdHkgYW5kIGhvdXIgb2YgdGhlIGRheQpgYGB7cn0KIyBDb21iaW5lIGJvdGggcGxvdHMgdG8gc2VlIHRoZSBidXNpZXN0IHRpbWVzIGZvciB0aGUgdXNlIG9mIHRoZSBiaWtlc2hhcmUgc2VydmljZSBwZXIgY2l0eSBhbmQgaG91ciBvZiB0aGUgZGF5CmdyaWQuYXJyYW5nZShxM18xLCBxM18yLCBuY29sID0gMikKYGBgCgojIyMgQ29tYmluZSBib3RoIGhpc3RvZ3JhbSBpbiBvbmUgcGxvdCB0byB2aWV3IHRoZSBidXNpZXN0IHRpbWUgY29tYmluZWQKYGBge3J9CiMgQ29tYmluZSBib3RoIGhpc3RvZ3JhbSBpbiBvbmUgcGxvdCB0byB2aWV3IHRoZSBidXNpZXN0IHRpbWUgY29tYmluZWQKZ2dwbG90KGFlcyhob3VyX3N0YXJ0KSwgZGF0YT1iaWtlc2hhcmUpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gIndoaXRlIiwgZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjQsIHBvc2l0aW9uPSJpZGVudGl0eSIpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeD1ob3VyX2VuZCksIGRhdGE9YmlrZXNoYXJlLCBiaW53aWR0aCA9IDEsIGNvbG9yID0gIndoaXRlIiwgZmlsbCA9ICJncmVlbiIsIGFscGhhID0gMC40LCBwb3NpdGlvbj0iaWRlbnRpdHkiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwgMjMsIDEpKSArCiAgeGxhYigiSG91ciBvZiB0aGUgRGF5IikgKyB5bGFiKCJOdW1iZXIgb2YgVHJpcHMiKSArCiAgZmFjZXRfd3JhcCh+Y2l0eSwgbnJvdyA9IDMsIHNjYWxlcz0iZnJlZV95IiwgbGFiZWxsZXIgPSBhc19sYWJlbGxlcihjKGNoaSA9ICJDaGljYWdvIiwgbnkgPSAiTmV3IFlvcmsiLCB3YXNoID0gIldhc2hpbmd0b24iKSkpCmBgYAoKIyMjIENvdW50IHRoZSBudW1iZXIgb2YgdHJpcHMgdGhhdCBTVEFSVEVEIGluIGVhY2ggaG91ciBvZiB0aGUgZGF5IGJ5IGNpdHkKYGBge3J9CiMgQ291bnQgdGhlIG51bWJlciBvZiB0cmlwcyB0aGF0IFNUQVJURUQgaW4gZWFjaCBob3VyIG9mIHRoZSBkYXkgYnkgY2l0eQpob3Vyc19zdGFydCA8LSBiaWtlc2hhcmUlPiUKICBncm91cF9ieShjaXR5LCBob3VyX3N0YXJ0KSU+JQogIHN1bW1hcmlzZShuX3N0YXJ0ID0gbGVuZ3RoKGlkKSkKaG91cnNfc3RhcnQKYGBgCgojIyMgRXh0cmFjdCB0aGUgaG91ciBvZiB0aGUgZGF5IHdpdGggdGhlIG1heGltdW0gYW1vdW50IG9mIHRyaXBzIFNUQVJURUQKYGBge3J9CiMgRXh0cmFjdCB0aGUgaG91ciBvZiB0aGUgZGF5IHdpdGggdGhlIG1heGltdW0gYW1vdW50IG9mIHRyaXBzIFNUQVJURUQKbWF4X3N0YXJ0X2hvdXJzIDwtIGhvdXJzX3N0YXJ0JT4lCiAgZ3JvdXBfYnkoY2l0eSklPiUKICBmaWx0ZXIobl9zdGFydD09bWF4KG5fc3RhcnQpKQptYXhfc3RhcnRfaG91cnMKYGBgCgojIyMgQ291bnQgdGhlIG51bWJlciBvZiB0cmlwcyB0aGF0IEVORCBpbiBlYWNoIGhvdXIgb2YgdGhlIGRheSBieSBjaXR5CmBgYHtyfQojIENvdW50IHRoZSBudW1iZXIgb2YgdHJpcHMgdGhhdCBFTkQgaW4gZWFjaCBob3VyIG9mIHRoZSBkYXkgYnkgY2l0eQpob3Vyc19lbmQgPC0gYmlrZXNoYXJlJT4lCiAgZ3JvdXBfYnkoY2l0eSwgaG91cl9lbmQpJT4lCiAgc3VtbWFyaXNlKG5fZW5kID0gbGVuZ3RoKGlkKSkKaG91cnNfZW5kCmBgYAoKIyMjIEV4dHJhY3QgdGhlIGhvdXIgb2YgdGhlIGRheSwgd2hlcmUgbW9zdCBvZiB0aGUgdHJpcHMgRU5ECmBgYHtyfQojIEV4dHJhY3QgdGhlIGhvdXIgb2YgdGhlIGRheSwgd2hlcmUgbW9zdCBvZiB0aGUgdHJpcHMgRU5ECm1heF9lbmRfaG91cnMgPC0gaG91cnNfZW5kJT4lCiAgZ3JvdXBfYnkoY2l0eSklPiUKICBmaWx0ZXIobl9lbmQ9PW1heChuX2VuZCkpCm1heF9lbmRfaG91cnMKYGBgCgojIyMgQ29tYmluZSBkYXRhIHNldHMgY29udGFpbmluZyB0aGUgbWF4aW11bSB2YWx1ZXMgdG8gcHJvdmlkZSBzdW1tYXJ5IHN0YXRpc3RpY3MKYGBge3J9CiMgQ29tYmluZSBkYXRhIHNldHMgY29udGFpbmluZyB0aGUgbWF4aW11bSB2YWx1ZXMgdG8gcHJvdmlkZSBzdW1tYXJ5IHN0YXRpc3RpY3MKbWF4X2hvdXJzIDwtIG1heF9zdGFydF9ob3VycyU+JQogIGZ1bGxfam9pbihtYXhfZW5kX2hvdXJzLCBieSA9ICJjaXR5IikKbWF4X2hvdXJzCmBgYAoKIyMjIEFuc3dlcjogVGhlIGhvdXIgb2YgdGhlIGRheSwgd2hlbiB0aGUgYmlrZXNoYXJpbmcgc2VydmljZSBpcyBtb3N0IHVzZWQsIGlzIGluIHRoZSBhZnRlcm5vb24gZm9yIGJvdGggQ2hpY2FnbyBhbmQgTmV3IFlvcmsgQ2l0eS4gSW4gYm90aCBjaXRpZXMgdGhlIGJ1c2llc3QgaG91ciBvZiB0aGUgZGF5IGlzIGJldHdlZW4gNXBtIGFuZCA2cG0gd2l0aCByb3VnaGx5IDUwNTAgdHJpcHMgc3RhcnRpbmcgYW5kIGVuZGluZyBpbiBOZXcgWW9yayBDaXR5IGFuZCByb3VnaGx5IDkwMCBpbiBDaGljYWdvLgoKIyMjIEluIGNvbXBhcmlzaW9uLCBjdXN0b21lcnMgaW4gV2FzaGluZ3RvbiBELkMuIGFyZSBtb3JlIGluY2xpbmVkIHRvIHVzZSB0aGUgYmlrZXNoYXJpbmcgc2VydmljZSBpbiB0aGUgbW9ybmluZ3Mgd2l0aCA4YW0gYmVpbmcgcHJpbWUgdGltZSB3aXRoIGFsbW9zdCAxMC4wMDAgdHJpcHMuCgoKCiMgUXVlc3Rpb24gNAojIyBXaGF0IGlzIHRoZSBtb3N0IGNvbW1vbiB0cmlwIGZyb20gc3RhcnQgdG8gZW5kIGJ5IGNpdHkgYW5kIHdoYXQgaXMgdGhlIGF2ZXJhZ2UgdHJhdmVsIHRpbWUgb24gdGhpcyB0cmlwPwoKIyMgUTQuMSAtIEZpcnN0IFBhcnQ6IEZpbmQgbW9zdCBwb3B1bGFyIHN0YXJ0aW5nIHN0YXRpb24gcGVyIGNpdHkKCiMjIyBTdW0gdXAgYWxsIG51bWJlciBvZiB0aW1lcyBhIHN0YXRpb24gaGFzIGJlZW4gdGhlIHN0YXJ0aW5nIHBvaW50IG9mIGEgdHJpcCBpbiBlYWNoIGNpdHkgYW5kIHJlbmFtaW5nIHRoZSBuZXdseSBjcmVhdGVkIHZhcmlhYmxlCmBgYHtyfQojIFN1bSB1cCBhbGwgbnVtYmVyIG9mIHRpbWVzIGEgc3RhdGlvbiBoYXMgYmVlbiB0aGUgc3RhcnRpbmcgcG9pbnQgb2YgYSB0cmlwIGluIGVhY2ggY2l0eSBhbmQgcmVuYW1pbmcgdGhlIG5ld2x5IGNyZWF0ZWQgdmFyaWFibGUKY291bnRfc3RhcnQgPC0gZGRwbHkoYmlrZXNoYXJlLCAuKFN0YXJ0LlN0YXRpb24sIGNpdHkpLCBucm93KQpjb3VudF9zdGFydCA8LSBjb3VudF9zdGFydCU+JQogIHJlbmFtZShuID0gVjEpCmNvdW50X3N0YXJ0CmBgYAoKIyMjIENvbnZlcnQgdGhlIHZhcmlhYmxlIG4gKG51bWJlciBvZiB0cmlwcyB0aGF0IHN0YXJ0ZWQgYXQgYSBzdGF0aW9uKSB0byBudW1lcmljIHZhcmlhYmxlIGZvciBlYXNpZXIgaGFuZGxpbmcKYGBge3J9CiMgQ29udmVydCB0aGUgdmFyaWFibGUgbiAobnVtYmVyIG9mIHRyaXBzIHRoYXQgc3RhcnRlZCBhdCBhIHN0YXRpb24pIHRvIG51bWVyaWMgdmFyaWFibGUgZm9yIGVhc2llciBoYW5kbGluZwpjb3VudF9zdGFydCRuIDwtIGFzLmRvdWJsZShhcy5jaGFyYWN0ZXIoY291bnRfc3RhcnQkbikpCmNvdW50X3N0YXJ0JG4gPC0gYXMubnVtZXJpYyhjb3VudF9zdGFydCRuKQpzdHIoY291bnRfc3RhcnQpCmBgYAoKIyMjIENvbnZlcnQgdGhlIHZhcmlhYmxlIFN0YXJ0LlN0YXRpb24gZnJvbSBmYWN0b3IgdG8gY2hhcmFjdGVyIGZvciBlYXNpZXIgaGFuZGxpbmcKYGBge3J9CiMgQ29udmVydCB0aGUgdmFyaWFibGUgU3RhcnQuU3RhdGlvbiBmcm9tIGZhY3RvciB0byBjaGFyYWN0ZXIgZm9yIGVhc2llciBoYW5kbGluZwpjb3VudF9zdGFydCRTdGFydC5TdGF0aW9uIDwtIGFzLmNoYXJhY3Rlcihhcy5mYWN0b3IoY291bnRfc3RhcnQkU3RhcnQuU3RhdGlvbikpCnN0cihjb3VudF9zdGFydCkKYGBgCgojIyMgR2V0IHRoZSBuYW1lIG9mIHRoZSBzdGF0aW9ucywgd2hlcmUgbW9zdCBvZiB0aGUgdHJpcHMgaGF2ZSBzdGFydGVkIGZvciBlYWNoIG9mIHRoZSBjaXRpZXMKYGBge3J9CiMgR2V0IHRoZSBuYW1lIG9mIHRoZSBzdGF0aW9ucywgd2hlcmUgbW9zdCBvZiB0aGUgdHJpcHMgaGF2ZSBzdGFydGVkIGZvciBlYWNoIG9mIHRoZSBjaXRpZXMKdG9wX3N0YXJ0X3N0YXRpb25zIDwtIGNvdW50X3N0YXJ0JT4lCiAgZ3JvdXBfYnkoY2l0eSklPiUKICBmaWx0ZXIobj09bWF4KG4pKSU+JQogIHVuZ3JvdXAoKQp0b3Bfc3RhcnRfc3RhdGlvbnMKYGBgCgojIyMgUmVvcmRlciBkYXRhIGZyYW1lIGluIGRlc2NlbmRpbmcgb3JkZXIKYGBge3J9CiMgUmVvcmRlciBkYXRhIGZyYW1lIGluIGRlc2NlbmRpbmcgb3JkZXIKdG9wX3N0YXJ0X3N0YXRpb25zIDwtIHRvcF9zdGFydF9zdGF0aW9ucyU+JQogIGFycmFuZ2UoZGVzYyhuKSklPiUKICBtdXRhdGUoY2l0eSA9IGZhY3RvcihjaXR5LCBsZXZlbHM9Yygid2FzaCIsICJueSIsICJjaGkiKSkpCnRvcF9zdGFydF9zdGF0aW9ucwpgYGAKCiMjIyBDcmVhdGUgcGxvdCB3aXRoIHRoZSB0b3Agc3RhcnQgc3RhdGlvbnMgcGVyIGNpdHkKYGBge3J9CiMgQ3JlYXRlIHBsb3Qgd2l0aCB0aGUgdG9wIHN0YXJ0IHN0YXRpb25zIHBlciBjaXR5CmdncGxvdChhZXMoeD1jaXR5LCB5PW4sIGxhYmVsPVN0YXJ0LlN0YXRpb24pLCBkYXRhPXRvcF9zdGFydF9zdGF0aW9ucykgKwogIGdlb21fY29sKGNvbG9yPSJibGFjayIsIGZpbGw9ImJsdWUiKSArCiAgZ2VvbV90ZXh0KHNpemU9MywgY29sb3IgPSAid2hpdGUiLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC43KSkgKwogIGdndGl0bGUoIk1vc3QgUG9wdWxhciBTdGFydCBTdGF0aW9uIEJ5IENpdHkiKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShuYW1lID0gIkNpdHkiLCBsYWJlbHMgPSBjKCJXYXNoaW5ndG9uIiwgIk5ldyBZb3JrIENpdHkiLCAiQ2hpY2FnbyIpKSArCiAgeWxhYigiTnVtYmVyIG9mIHRyaXBzIHN0YXJ0ZWQgYXQgc3RhdGlvbiIpCmBgYAoKCiMjIFE0LjIgLSBTZWNvbmQgUGFydDogRmluZCBtb3N0IHBvcHVsYXIgZW5kIHN0YXRpb25zIHBlciBjaXR5LCBnaXZlbiB0cmlwcyBoYXZlIHN0YXJ0ZWQgYXQgdGhlIG1vc3QgcG9wdWxhciBzdGFydGluZyBzdGF0aW9ucwoKIyMjIFN1YnNldCBvcmlnaW5hbCBiaWtlc2hhcmUgZGF0YSBzZXQgdG8gb25seSBpbmNsdWRlIG9ic2VydmF0aW9ucyBmcm9tIHRoZSBtb3N0IHBvcHVsYXIgc3RhcnRpbmcgc3RhdGlvbnMgcGVyIGNpdHkKYGBge3J9CiMgU3Vic2V0IG9yaWdpbmFsIGJpa2VzaGFyZSBkYXRhIHNldCB0byBvbmx5IGluY2x1ZGUgb2JzZXJ2YXRpb25zIGZyb20gdGhlIG1vc3QgcG9wdWxhciBzdGFydGluZyBzdGF0aW9ucyBwZXIgY2l0eQpjb3VudF9lbmQgPC0gYmlrZXNoYXJlJT4lCiAgZmlsdGVyKFN0YXJ0LlN0YXRpb24gJWluJSBjKCJDb2x1bWJ1cyBDaXJjbGUgLyBVbmlvbiBTdGF0aW9uIiwgIlBlcnNoaW5nIFNxdWFyZSBOb3J0aCIsICJDbGludG9uIFN0ICYgV2FzaGluZ3RvbiBCbHZkIikpCmNvdW50X2VuZApgYGAKCiMjIyBRdWlja2x5IGNoZWNrIGlmIG51bWJlciBvZiBzdGFydGVkIHRyaXBzIGlzIGVxdWFsIGluIGJvdGggdGhlIHRvcF9zdGFydF9zdGF0aW9ucyBkYXRhIHNldCBhbmQgdGhlIG5ld2x5IGNyZWF0ZWQgY291bnRfZW5kIGRhdGEgc2V0IGJ5IHVzaW5nIHRoZSBmdW5jdGlvbiBidWlsdCBpbiB0aGUgcHJlcGFyYXRpb24gcGFydCBvZiB0aGUgZXhlcmNpc2UKYGBge3J9CiMgUXVpY2tseSBjaGVjayBpZiBudW1iZXIgb2Ygc3RhcnRlZCB0cmlwcyBpcyBlcXVhbCBpbiBib3RoIHRoZSB0b3Bfc3RhcnRfc3RhdGlvbnMgZGF0YSBzZXQgYW5kIHRoZSBuZXdseSBjcmVhdGVkIGNvdW50X2VuZCBkYXRhIHNldCBieSB1c2luZyB0aGUgZnVuY3Rpb24gYnVpbHQgaW4gdGhlIHByZXByYXRpb24gcGFydCBvZiB0aGUgZXhlcmNpc2UKeDIgPC0gbnJvdyhjb3VudF9lbmQpCnkyIDwtIHN1bSh0b3Bfc3RhcnRfc3RhdGlvbnMkbikKY2hlY2soeDIsIHkyKQpgYGAKCiMjIyBTdW0gdXAgdGhlIG51bWJlciBvZiB0aW1lcyBhIHN0YXRpb24gaGFzIGJlZW4gdGhlIGVuZCBwb2ludCBvZiBhIHRyaXAgZ2l2ZW4gb25lIG9mIHRoZSB0b3Agc3RhcnRpbmcgc3RhdGlvbnMgd2FzIHRoZSBzdGFydGluZyBwb2ludCBvZiBhIHRyaXAgZm9yIGVhY2ggb2YgdGhlIGNpdHkKYGBge3J9CiMgU3VtIHVwIHRoZSBudW1iZXIgb2YgdGltZXMgYSBzdGF0aW9uIGhhcyBiZWVuIHRoZSBlbmQgcG9pbnQgb2YgYSB0cmlwIGdpdmVuIG9uZSBvZiB0aGUgdG9wIHN0YXJ0aW5nIHN0YXRpb25zIHdhcyB0aGUgc3RhcnRpbmcgcG9pbnQKIyBvZiBhIHRyaXAgZm9yIGVhY2ggb2YgdGhlIGNpdHkKY291bnRfZW5kIDwtIGRkcGx5KGNvdW50X2VuZCwgLihFbmQuU3RhdGlvbiwgY2l0eSwgU3RhcnQuU3RhdGlvbiksIG5yb3cpCmNvdW50X2VuZCA8LSBjb3VudF9lbmQlPiUKICByZW5hbWUobiA9IFYxKQpjb3VudF9lbmQKYGBgCgojIyMgQ29udmVydCB0aGUgdmFyaWFibGUgbiAobnVtYmVyIG9mIHRyaXBzIHRoYXQgZW5kZWQgYXQgYSBzdGF0aW9uKSB0byBudW1lcmljIHZhcmlhYmxlIGZvciBlYXNpZXIgaGFuZGxpbmcKYGBge3J9CiMgQ29udmVydCB0aGUgdmFyaWFibGUgbiAobnVtYmVyIG9mIHRyaXBzIHRoYXQgZW5kZWQgYXQgYSBzdGF0aW9uKSB0byBudW1lcmljIHZhcmlhYmxlIGZvciBlYXNpZXIgaGFuZGxpbmcKY291bnRfZW5kJG4gPC0gYXMuZG91YmxlKGFzLmNoYXJhY3Rlcihjb3VudF9lbmQkbikpCmNvdW50X2VuZCRuIDwtIGFzLm51bWVyaWMoY291bnRfZW5kJG4pCnN0cihjb3VudF9lbmQpCmBgYAoKIyMjIENvbnZlcnQgdGhlIHZhcmlhYmxlIEVuZC5TdGF0aW9uIGZyb20gZmFjdG9yIHRvIGNoYXJhY3RlciBmb3IgZWFzaWVyIGhhbmRsaW5nCmBgYHtyfQojIENvbnZlcnQgdGhlIHZhcmlhYmxlIEVuZC5TdGF0aW9uIGZyb20gZmFjdG9yIHRvIGNoYXJhY3RlciBmb3IgZWFzaWVyIGhhbmRsaW5nCmNvdW50X2VuZCRFbmQuU3RhdGlvbiA8LSBhcy5jaGFyYWN0ZXIoYXMuZmFjdG9yKGNvdW50X2VuZCRFbmQuU3RhdGlvbikpCnN0cihjb3VudF9lbmQpCmBgYAoKIyMjIEdldCB0aGUgY291bnRzIG9mIHRoZSBzdGF0aW9ucywgd2hlcmUgbW9zdCBvZiB0aGUgdHJpcHMgZW5kZWQsIGdpdmVuIHRoZXkgc3RhcnRlZCBhdCB0aGUgbW9zdCBwb3B1bGFyIHN0YXJ0aW5nIHN0YXRpb24gZm9yIGVhY2ggY2l0eQpgYGB7cn0KIyBHZXQgdGhlIGNvdW50cyBvZiB0aGUgc3RhdGlvbnMsIHdoZXJlIG1vc3Qgb2YgdGhlIHRyaXBzIGVuZGVkLCBnaXZlbiB0aGV5IHN0YXJ0ZWQgYXQgdGhlIG1vc3QgcG9wdWxhciBzdGFydGluZyBzdGF0aW9uIGZvciBlYWNoIGNpdHkKcG9wX3N0YXRpb25zX2VuZCA8LSBjb3VudF9lbmQlPiUKICBncm91cF9ieShjaXR5LCBTdGFydC5TdGF0aW9uKSU+JQogIHN1bW1hcmlzZShtYXggPSBtYXgobikpCnBvcF9zdGF0aW9uc19lbmQKYGBgCgojIyMgIyBHZXQgdGhlIG5hbWUgb2YgdGhlIHN0YXRpb25zLCB3aGVyZSBtb3N0IG9mIHRoZSB0cmlwcyBoYXZlIGVuZGVkLCBnaXZlbiB0aGV5IHN0YXJ0ZWQgYXQgdGhlIG1vc3QgcG9wdWxhciBzdGFydGluZyBzdGF0aW9uIGZvciBlYWNoIGNpdHkKYGBge3J9CiMgR2V0IHRoZSBuYW1lIG9mIHRoZSBzdGF0aW9ucywgd2hlcmUgbW9zdCBvZiB0aGUgdHJpcHMgaGF2ZSBlbmRlZCwgZ2l2ZW4gdGhleSBzdGFydGVkIGF0IHRoZSBtb3N0IHBvcHVsYXIgc3RhcnRpbmcgc3RhdGlvbiAKIyBmb3IgZWFjaCBjaXR5Cm55X2VuZF9zdGF0aW9uIDwtIGNvdW50X2VuZCU+JQogIGZpbHRlcihjaXR5ID09ICJueSIgJiBuID09IDI0KQpueV9lbmRfc3RhdGlvbgoKd2FzaF9lbmRfc3RhdGlvbiA8LSBjb3VudF9lbmQlPiUKICBmaWx0ZXIoY2l0eSA9PSAid2FzaCIgJiBuID09IDEwNykKd2FzaF9lbmRfc3RhdGlvbgoKY2hpX2VuZF9zdGF0aW9uIDwtIGNvdW50X2VuZCU+JQogIGZpbHRlcihjaXR5ID09ICJjaGkiICYgbiA9PSAxMSkKY2hpX2VuZF9zdGF0aW9uCmBgYAoKIyMjIENvbWJpbmUgaW5kaXZpZHVhbCByZXN1bHRzIGZyb20gZWFjaCBjaXR5IHRvIGEgZGF0YSBmcmFtZSBzaG93aW5nIHRoZSBtb3N0IHBvcHVsYXIgdHJpcHMgcGVyIGNpdHkKYGBge3J9CiMgQ29tYmluZSBpbmRpdmlkdWFsIHJlc3VsdHMgZnJvbSBlYWNoIGNpdHkgdG8gYSBkYXRhIGZyYW1lIHNob3dpbmcgdGhlIG1vc3QgcG9wdWxhciB0cmlwcyBwZXIgY2l0eQp0b3BfdHJpcHMgPC0gcmJpbmQuZmlsbChueV9lbmRfc3RhdGlvbiwgd2FzaF9lbmRfc3RhdGlvbiwgY2hpX2VuZF9zdGF0aW9uKSU+JQogIGFycmFuZ2UoZGVzYyhuKSklPiUKICBtdXRhdGUoY2l0eSA9IGZhY3RvcihjaXR5LCBsZXZlbHM9Yygid2FzaCIsICJueSIsICJjaGkiKSkpJT4lCiAgcmVuYW1lKENpdHkgPSBjaXR5LCBOdW1iZXJPZlRyaXBzID0gbikKdG9wX3RyaXBzCmBgYAoKIyMjIFJlYXJyYW5nZSBjb2x1bW4gb3JkZXIgdG8gaW5jcmVhc2UgcmVhZGFiaWxpdHkgb2YgZGF0YSBmcmFtZQpgYGB7cn0KIyBSZWFycmFuZ2UgY29sdW1uIG9yZGVyIHRvIGluY3JlYXNlIHJlYWRhYmlsaXR5IG9mIGRhdGEgZnJhbWUKY29sX29yZGVyIDwtIGMoIkNpdHkiLCAiU3RhcnQuU3RhdGlvbiIsICJFbmQuU3RhdGlvbiIsICJOdW1iZXJPZlRyaXBzIikKdG9wX3RyaXBzIDwtIHRvcF90cmlwc1ssIGNvbF9vcmRlcl0KdG9wX3RyaXBzCmBgYAoKIyMjIENvbWJpbmUgU3RhcnQuU3RhdGlvbiBhbmQgRW5kLlN0YXRpb24gdG8gVHJpcCB2YXJpYWJsZQpgYGB7cn0KIyBDb21iaW5lIFN0YXJ0LlN0YXRpb24gYW5kIEVuZC5TdGF0aW9uIHRvIFRyaXAgdmFyaWFibGUKdG9wX3RyaXBzIDwtIHRyYW5zZm9ybSh0b3BfdHJpcHMsIFRyaXAgPSBwYXN0ZShTdGFydC5TdGF0aW9uLCBFbmQuU3RhdGlvbiwgc2VwID0gIiAtICIpKQp0b3BfdHJpcHMKYGBgCgojIyMgQ3JlYXRlIHBsb3Qgc2hvd2luZyB0aGUgbW9zdCBwb3B1bGFyIHRyaXBzIHBlciBjaXR5CmBgYHtyfQojIENyZWF0ZSBwbG90IHNob3dpbmcgdGhlIG1vc3QgcG9wdWxhciB0cmlwcyBwZXIgY2l0eQpnZ3Bsb3QoYWVzKHg9Q2l0eSwgeT1OdW1iZXJPZlRyaXBzLCBjb2xvcj1UcmlwKSwgZGF0YT10b3BfdHJpcHMpICsKICBnZW9tX2NvbChjb2xvcj0iYmxhY2siLCBmaWxsPSJibHVlIikgKwogIGdndGl0bGUoIk1vc3QgUG9wdWxhciBUcmlwcyBCeSBDaXR5IikgKwogIGdlb21fdGV4dChsYWJlbCA9IHRvcF90cmlwcyRTdGFydC5TdGF0aW9uLCBzaXplPTMsIGNvbG9yID0gIndoaXRlIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNykpICsKICBnZW9tX3RleHQobGFiZWwgPSAiLSIsIHNpemU9MywgY29sb3IgPSAid2hpdGUiLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC42KSkgKwogIGdlb21fdGV4dChsYWJlbCA9IHRvcF90cmlwcyRFbmQuU3RhdGlvbiwgc2l6ZT0zLCBjb2xvciA9ICJ3aGl0ZSIsIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjQpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwgMTIwLCAxMCkpICsKICBzY2FsZV94X2Rpc2NyZXRlKG5hbWUgPSAiQ2l0eSIsIGxhYmVscyA9IGMoIldhc2hpbmd0b24iLCAiTmV3IFlvcmsgQ2l0eSIsICJDaGljYWdvIikpICsKICB5bGFiKCJOdW1iZXIgb2YgVHJpcHMiKQpgYGAKCiMjIyBQcm92aWRlIHN1bW1hcnkgc3RhdGlzdGljcwpgYGB7cn0KIyBQcm92aWRlIHN1bW1hcnkgc3RhdGlzdGljcwp0b3BfdHJpcHMKYGBgCgojIyMgQW5zd2VyOiBUaGUgbW9zdCBwb3B1bGFyIHRyaXAgZm9yIFdhc2hpbmd0b24gRC5DLiBpcyB0aGUgdHJpcCBmcm9tIENvbHVtYnVzIENpcmNsZSAvIFVuaW9uIFN0YXRpb24gdG8gOHRoICYgRiBTdCBORSwgd2hpY2ggd2FzIHRha2VuIDEwNyB0aW1lcyBkdXJpbmcgdGhlIG9ic2VydmF0aW9uIHBlcmlvZC4gVGhlIG1vc3QgcG9wdWxhciB0cmlwIGluIE5ldyBZb3JrIENpdHkgd2FzIGJldHdlZW4gUGVyc2hpbmcgU3F1YXJlIE5vcnRoIGFuZCBXIDMzIFN0ICYgNyBBdmUgd2l0aCAyNCB0cmlwcyBhbmQgaW4gQ2hpY2FnbyB0aGUgbW9zdCBwb3B1bGFyIHRyaXAgd2FzIGZyb20gQ2xpbnRvbiBTdCAmIFdhc2hpbmd0b24gQmx2ZCB0byBNaWNoaWdhbiBBdmUgJiBXYXNoaW5ndG9uIFN0IHdpdGggMTEgdHJpcHMgaW4gdG90YWwuCgoKCgoKCg==